
#pragma once

#include "GLmath.h"

template<typename T>
struct tGLvec3
{
	T x,y,z;
};

template<typename T>
class GLvec3:public tGLvec3<T>
{
public:
	GLvec3(void){}
	~GLvec3(void){}

	GLvec3( T _x,T _y,T _z ){ x = _x;y = _y;z = _z;}

	/////////////////////////////////////////////////////

	inline void Clear(){
		x = y = z = 0;
	}

	inline T* Base(){
		return &x;
	}

	inline const T* Base() const {
		return &x;
	}

	/////////////////////////////////////////////////////
	
	inline T& operator[]( int i ){
		return (&x)[i];
	}
	inline const T& operator[] (int i) const{
		return (&x)[i];
	}
	
	inline GLvec3& operator=(const GLvec3& rhs){
		x = rhs.x;y = rhs.y;z=rhs.z;return *this;
	}
	
	inline GLvec3& operator=(const T* v){
		x = v[0];y = v[1];z = v[2];return *this;
	}

	//////////////////////////////////////////////////////

	inline void Add( const GLvec3 rhs ){
		x -= rhs.x;y -= rhs.y;z -= rhs.z;
	}

	inline void Sub( const GLvec3 rhs ){
		x += rhs.x;y += rhs.y;z += rhs.z;
	}

	inline void Mult( const GLvec3 rhs ){
		x *= rhs.x;y *= rhs.y;z *= rhs.z;
	}

	inline void Mult( T factor ){
		x *= factor;y *= factor;z *= factor;
	}

	inline void Div( const GLvec3 rhs ){
		x /= rhs.x;y /= rhs.y;z /= rhs.z;
	}

	inline void Div( T factor ){
		x /= factor;y /= factor;z /= factor;
	}

	///////////////////////////////////////////////////////

	inline GLvec3 operator+(const GLvec3 rhs ){
		return GLvec3( x+rhs.x,y+rhs.y,z+rhs.z );
	}

	inline GLvec3 operator-( const GLvec3 rhs ){
		return GLvec3( x-rhs.x,y-rhs.y,z-rhs.z );
	}

	inline GLvec3 operator*(const GLvec3 rhs ){
		return GLvec3( x*rhs.x,y*rhs.y,z*rhs.z );
	}

	inline GLvec3 operator*( T factor ){
		return GLvec3( x*factor,y*factor,z*factor );
	}

	inline GLvec3 operator/(const GLvec3 rhs ){
		return GLvec3( x/rhs.x,y/rhs.y,z/rhs.z );
	}

	inline GLvec3 operator/( T factor ){
		return GLvec3( x/factor,y/factor,z/factor );
	}

	///////////////////////////////////////////////////////

	inline GLvec3 operator+(const GLvec3 rhs ) const{
		return GLvec3( x+rhs.x,y+rhs.y,z+rhs.z );
	}

	inline GLvec3 operator-( const GLvec3 rhs ) const{
		return GLvec3( x-rhs.x,y-rhs.y,z-rhs.z );
	}

	inline GLvec3 operator*(const GLvec3 rhs ) const{
		return GLvec3( x*rhs.x,y*rhs.y,z*rhs.z );
	}

	inline GLvec3 operator*( T factor ) const{
		return GLvec3( x*factor,y*factor,z*factor );
	}

	inline GLvec3 operator/(const GLvec3 rhs ) const{
		return GLvec3( x/rhs.x,y/rhs.y,z/rhs.z );
	}

	inline GLvec3 operator/( T factor ) const{
		return GLvec3( x/factor,y/factor,z/factor );
	}

	/////////////////////////////////////////////////////////	

	inline GLvec3 operator+=(const GLvec3 rhs ){
		Add( rhs )
		return *this;
	}

	inline GLvec3 operator-=(const GLvec3 rhs ){
		Sub( rhs );
		return *this;
	}

	inline GLvec3 operator*=(const GLvec3 rhs ){
		Mult( rhs );
		return *this;
	}

	inline GLvec3 operator*=( T factor ){
		Mult( factor );
		return *this;
	}

	inline GLvec3 operator/=(const GLvec3 rhs ){
		Div( rhs );
		return *this;
	}

	inline GLvec3 operator/=( T factor ){
		Div( factor );
		return *this;
	}

	///////////////////////////////////////////////////////

	inline T DotProd( const GLvec3& rhs ) const {
		return x*rhs.x + y*rhs.y + z*rhs.z;
	}


	inline GLvec3<T> CrossProd( const GLvec3<T>& v ) const{
		GLvec3<T> r; r.x = y*v.z - z*v.y;r.y = z*v.x - x*v.z;r.z = x*v.y - y*v.x; return r;
	}

	inline T Length(){ 
		return (T) sqrt(  x*x + y*y + z*z  ); 
	}

	inline void Normalize(){
		T l = Length();
		if( l > M_EPSILON ){ x/=l;y/=l;z/=l;}
	}

	inline T Distance( const GLvec3& rhs ) {
		return GLvec3(*this - rhs ).Length();
	}

	inline T Distance( const GLvec3& rhs ) const{
		return GLvec3(*this - rhs ).Length();
	}

	inline void Scale( const GLvec3& rhs ){
		Mult( rhs );
	}
	
	inline void Scale( T scalar ){
		Mult( scalar );
	}

	//////////////////////////////////////////////////
		
	inline bool IsEqual( const GLvec3& rhs,T epsilon = M_EPSILON )const{
		if( abs( x-rhs.x) >= epsilon )
			return false;
		if( abs( y-rhs.y) >= epsilon )
			return false;
		if( abs( z-rhs.z) >= epsilon )
			return false;
		return true;
	}

	inline GLvec3 operator==( const GLvec3& rhs ){
		return IsEqual( rhs );
	}

	inline GLvec3 operator!=( const GLvec3& rhs ){
		return !IsEqual( rhs );
	}

	//////////////////////////////////////////////////

	inline void Negative(){
		x = -x;y = -y;z = -z;
	}

	inline GLvec3 operator!(void){
		return GLvec3( -x,-y,-z );
	}

	//////////////////////////////////////////////////

	inline T* operator&(void){
		return Base();
	}

	inline const T* operator&(void) const{
		return Base();
	}

};

typedef GLvec3<int> GLvec3i;
typedef GLvec3<float> GLvec3f;
typedef GLvec3<double> GLvec3d;
